﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;

namespace Tzync
{
    public partial class SyncUIForm : Form, IObserver, IDisplay
    {
        private SyncTool syncTool;
        private Profile profile;
        private SystemType systemName;
        private String systemId;
        private String rootAddress; //path of folder chosen by user
        private ConflictUIForm resolveForm;  //UI to resolve conflicts
        private bool firstSyncA; //1st time sync at A
        private bool firstSyncB;    //1st time sync at B
        private Difference difference;    //a difference
        private List<Difference> conflictList;  //list of conflicts
        private List<Difference> nonConflictList;  //list of non-conflicts
        private bool isAnalyzed;    //whether analyze has already been done

        public SyncUIForm(SyncTool newSyncTool)
        {
            syncTool = newSyncTool;
            profile = syncTool.GetProfile();
            systemName = profile.SystemName;
            if (systemName == SystemType.Host)
                rootAddress = profile.RootAddressA;
            else
                rootAddress = profile.RootAddressB;
            syncTool.RegisterObserver(this);    //UI observes subjects for changes

            difference = null;
            nonConflictList = new List<Difference>();
            conflictList = new List<Difference>();
            firstSyncA = profile.FirstTime;    //find out if it's 1st sync at A
            firstSyncB = profile.SecondTime;  //find out if it's 1st sync at B
            isAnalyzed = false;

            InitializeComponent();
        }

        private void SyncUIForm_Load(object sender, EventArgs e)
        {
            mainLabel.Text = "Profile: " + profile.ProfileName;
            infoLabel.Text = "";
            ResizeColumns();

            //tooltips
            logoToolTip.SetToolTip(tzyncLogoPB, "Click here to visit Tzync's website!");
            backBtnToolTip.SetToolTip(backBtn, "Click here to go back to the Main screen");
            analyzeBtnToolTip.SetToolTip(analyzeBtn, "Click here to analyze for changes without syncing");
            syncBtnToolTip.SetToolTip(syncBtn, "Click here to start syncing!");
            filterBtnToolTip.SetToolTip(filterBtn, "Click here to include or exclude files to sync");
            rollbackBtnToolTip.SetToolTip(rollbackBtn, "Click here to restore files to a previous version");
            helpBtnToolTip.SetToolTip(helpBtn, "Click here to get online help for Tzync!");
            quitBtnToolTip.SetToolTip(quitBtn, "Why leave when you are having fun?");
        }

        private void SyncUIForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.DialogResult = DialogResult.OK;
        }

        //home button
        private void backBtn_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void tzyncLogoPB_Click(object sender, EventArgs e)
        {
            infoLabel.Text = "";
            Process.Start("http://code.google.com/p/tzync/");
        }

        private void filterBtn_Click(object sender, EventArgs e)
        {
            infoLabel.Text = "";
            this.Visible = false;
            List<string> fileFilters = new List<string>(syncTool.getFileFilter());
            List<string> advancedFilters = new List<string>(syncTool.getAdvancedFilter());
            syncTool.FlushFilter();
            FilterUIForm filterUI = new FilterUIForm(syncTool, fileFilters, advancedFilters);
            DialogResult dialog = filterUI.ShowDialog();
            if (dialog == DialogResult.OK)
                this.Visible = true;
        }

        //analyze button
        private void analyzeBtn_Click(object sender, EventArgs e)
        {
            diffLV.Items.Clear();
            conflictList.Clear();
            nonConflictList.Clear();
            infoLabel.Text = "Analyze in progress...";
            Cursor.Current = Cursors.WaitCursor;
            syncTool.LoadMetadata();
            try
            {
                syncTool.Analyze();    //start analyze
            }
            catch (ArgumentNullException)
            {
                infoLabel.Text = "Metadata corrupted! Please delete this profile!";
            }

            isAnalyzed = true;            

            Cursor.Current = Cursors.Default;
            resultLabel.Text = "Results of Analyze";
            infoLabel.Text = "We are done analyzing!";
        }

        //sync button
        private void syncBtn_Click(object sender, EventArgs e)
        {            
            if (!isAnalyzed)
             {
                diffLV.Items.Clear();
                conflictList.Clear();
                nonConflictList.Clear();
             }

            infoLabel.Text = "Sync in progress...";
            Cursor.Current = Cursors.WaitCursor;

            if (!isAnalyzed)
            {
                syncTool.LoadMetadata();
                try
                {
                    syncTool.Analyze();
                }
                catch (ArgumentNullException a)
                {
                    if (a.Message == "sysFiles")
                        infoLabel.Text = "Metadata has been corrupted! Please delete this profile!";
                }
            }
            try
            {
                syncTool.Sync();   //start sync
            }
            catch (DiskSpaceNotEnoughException)
            {
                infoLabel.Text = "Your Hard disk space is not enough. Clear ur disk space and sync again.";
            }
            Cursor.Current = Cursors.Default;
            resultLabel.Text = "Results of Sync";

            if (conflictList.Count > 0) //informs user of conflicts found
            {
                DialogResult syncDialog = MessageBox.Show("We are done synchronizing but there are conflicts found! Click OK to resolve them.",
                        "Important!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                if (syncDialog == DialogResult.OK)
                {
                    this.Visible = false;
                    resolveForm = new ConflictUIForm(syncTool, conflictList);
                    DialogResult dialog = resolveForm.ShowDialog();   //displays resolve conflict screen
                    if (dialog == DialogResult.OK)
                    {
                        this.Visible = true;
                        infoLabel.Text = "Conflicts have been resolved!";
                        diffLV.Items.Clear();
                        DisplayResults();   //displays results of conflict resolutions
                    }
                }
            }
            else
                infoLabel.Text = "We are done synchronizing!";

            isAnalyzed = false;
        }

        private void rollbackBtn_Click(object sender, EventArgs e)
        {
            infoLabel.Text = "";
            this.Visible = false;
            RollbackUIForm rollbackUI = new RollbackUIForm(syncTool);
            DialogResult dialog = rollbackUI.ShowDialog(); ;
            if (dialog == DialogResult.OK)
                this.Visible = true;
        }

        private void quitBtn_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        //help button
        private void helpBtn_Click(object sender, EventArgs e)
        {
            infoLabel.Text = "";
            Process.Start("http://tzync.googlecode.com/files/Tzync_User_Guide_V1.pdf");
        }

        //tells UI to update its display
        public void Update(ISubject subject)
        {
            infoLabel.Text = "";
            DifferenceSubject ds = (DifferenceSubject)subject;
            difference = ds.GetDifference();    //a new difference has been found
            Display();  //display it
        }

        //displays content
        public void Display()
        {
            infoLabel.Text = "";
            DisplayDifference(false);
            ResizeColumns();
        }

        //displays differences found in listview
        private void DisplayDifference(bool conflictResolved)
        {
            infoLabel.Text = "";
            ListViewItem differenceItems = new ListViewItem();
            TreeNode tNode = new TreeNode();
            bool toAdd = false;

            Type typeOfDifference = difference.GetItemType();   //file or folder
            Pair<DirectionType, OperationType> differenceDetails = difference.GetPairType();

            //display conflicts
            if (difference.GetPairType().First == DirectionType.Nil)
                toAdd = AddConflict(differenceItems, typeOfDifference, differenceDetails, tNode, conflictResolved);

            //display non-conflicts
            else
                toAdd = AddNonConflict(differenceItems, typeOfDifference, differenceDetails, tNode, conflictResolved);

            if (toAdd)
                diffLV.Items.Add(differenceItems);  //display differences
        }

        //displays results after resolution of conflicts
        private void DisplayResults()
        {
            infoLabel.Text = "";
            for (int i = 0; i < conflictList.Count; i++)  //display resolved conflicts
            {
                difference = conflictList[i];
                DisplayDifference(true);
            }

            for (int i = 0; i < nonConflictList.Count; i++) //display non-conflicts
            {
                difference = nonConflictList[i];
                DisplayDifference(true);
            }
        }

        //displays conflicts found
        private bool AddConflict(ListViewItem items, Type typeOfDifference, Pair<DirectionType, OperationType> differenceDetails, TreeNode tNode, bool conflictResolved)
        {
            if (typeOfDifference == Type.File)  //file
            {
                if (conflictResolved == false)
                    conflictList.Add(difference);

                FileInfo fileInfo = difference.GetSysFile();
                tNode = difference.GetPartnerFile();

                if (syncTool.GetSystemName() == SystemType.Host)    //A
                {
                    items.Text = tNode.getFullPathA(); //path of file 
                    items.SubItems.Add("" + tNode.getSyncA());  //last modified date of file
                }
                else if (syncTool.GetSystemName() == SystemType.Guest)  //B
                {
                    items.Text = tNode.getFullPathB(); //path of file
                    items.SubItems.Add("" + tNode.getSyncB());  //last modified date of file
                }

                //displays actions taken
                if (differenceDetails.Second == OperationType.CreationCreationConflict)
                    items.SubItems.Add("Conflict - file created on both computers");
                else if (differenceDetails.Second == OperationType.ModDeleteConflict)
                    items.SubItems.Add("Conflict - file modified on 1 computer and deleted on the other");
                else if (differenceDetails.Second == OperationType.ModModConflict)
                    items.SubItems.Add("Conflict - file modified on both computers");
                else if (differenceDetails.Second == OperationType.DeletionDeletionConflict)
                    items.SubItems.Add("Conflict - file deleted on both computers");

                items.SubItems.Add("To be resolved");     //direction of operation
                items.ForeColor = Color.Red;

                return true;
            }

            else if (typeOfDifference == Type.Folder)   //folder
            {
                //ignore del-del, mod-mod and creation-creation folder conflicts
                if (differenceDetails.Second != OperationType.DeletionDeletionConflict
                && differenceDetails.Second != OperationType.ModModConflict && differenceDetails.Second != OperationType.CreationCreationConflict)
                {
                    if (conflictResolved == false)
                        conflictList.Add(difference);

                    DirectoryInfo dirInfo = difference.GetSysFolder();
                    tNode = difference.GetPartnerFolder();

                    if (syncTool.GetSystemName() == SystemType.Host)    //A
                    {
                        items.Text = tNode.getFullPathA(); //path of folder  
                        items.SubItems.Add("" + tNode.getSyncA());  //last modified date of folder
                    }
                    else if (syncTool.GetSystemName() == SystemType.Guest)  //B
                    {
                        items.Text = tNode.getFullPathB(); //path of folder  
                        items.SubItems.Add("" + tNode.getSyncB());  //last modified date of folder
                    }

                    //displays actions taken
                    if (differenceDetails.Second == OperationType.ModDeleteConflict)
                        items.SubItems.Add("Conflict - folder modified on 1 computer and deleted on the other");
                    else if (differenceDetails.Second == OperationType.CreationCreationConflict)
                        items.SubItems.Add("Conflict - folder created on both computers");
                    else if (differenceDetails.Second == OperationType.DeletionDeletionConflict)
                        items.SubItems.Add("Conflict - folder deleted on both computers");

                    items.SubItems.Add("To be resolved");     //direction of operation
                    items.ForeColor = Color.Red;

                    return true;
                }
            }

            return false;
        }

        //displays non-conflicts found
        private bool AddNonConflict(ListViewItem items, Type typeOfDifference, Pair<DirectionType, OperationType> differenceDetails, TreeNode tNode, bool resolved)
        {
            if (typeOfDifference == Type.File)  //file
            {
                if (resolved == false)
                    nonConflictList.Add(difference);

                FileInfo fileInfo = difference.GetSysFile();
                tNode = difference.GetPartnerFile();

                if (syncTool.GetSystemName() == SystemType.Host)    //A
                {
                    items.Text = tNode.getFullPathA(); //path of file 
                    items.SubItems.Add("" + tNode.getSyncA());  //last modified date of file
                }
                else if (syncTool.GetSystemName() == SystemType.Guest)  //B
                {
                    items.Text = tNode.getFullPathB(); //path of file
                    items.SubItems.Add("" + tNode.getSyncB());  //last modified date of file
                }

                //displays action taken
                if (differenceDetails.Second == OperationType.Creation)
                    items.SubItems.Add("Create file");
                else if (differenceDetails.Second == OperationType.Deletion)
                    items.SubItems.Add("Delete file");
                else if (differenceDetails.Second == OperationType.Modification)
                    items.SubItems.Add("Modify file");
                else
                    items.SubItems.Add("Resolved conflict");

                //displays direction of sync
                if (differenceDetails.First == DirectionType.SystemToHD)
                    items.SubItems.Add("To other computer");
                else if (differenceDetails.First == DirectionType.HDToSystem)
                    items.SubItems.Add("To this computer");

                return true;
            }

            else if (typeOfDifference == Type.Folder)   //folder
            {
                if (differenceDetails.Second != OperationType.Modification)   //ignore folder modifications
                {
                    if (resolved == false)
                        nonConflictList.Add(difference);

                    DirectoryInfo dirInfo = difference.GetSysFolder();
                    tNode = difference.GetPartnerFolder();

                    if (syncTool.GetSystemName() == SystemType.Host)    //A
                    {
                        items.Text = tNode.getFullPathA(); //path of folder  
                        items.SubItems.Add("" + tNode.getSyncA());  //last modified date of folder
                    }
                    else if (syncTool.GetSystemName() == SystemType.Guest)  //B
                    {
                        items.Text = tNode.getFullPathB(); //path of folder  
                        items.SubItems.Add("" + tNode.getSyncB());  //last modified date of folder
                    }

                    //displays action taken
                    if (differenceDetails.Second == OperationType.Creation)
                        items.SubItems.Add("Create folder");
                    else if (differenceDetails.Second == OperationType.Deletion)
                        items.SubItems.Add("Delete folder");
                    else if (differenceDetails.Second == OperationType.None)
                        items.SubItems.Add("Resolved conflict");

                    //displays direction of sync
                    if (differenceDetails.First == DirectionType.SystemToHD)
                        items.SubItems.Add("To other computer");
                    else if (differenceDetails.First == DirectionType.HDToSystem)
                        items.SubItems.Add("To this computer");

                    return true;
                }
            }

            return false;
        }

        //auto resize columns
        private void ResizeColumns()
        {
            int contentWidth, headerWidth;
            ColumnHeaderAutoResizeStyle headerResize;

            for (int index = 0; index < diffLV.Columns.Count; index++)
            {
                ColumnHeader header = diffLV.Columns[index];
                header.AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
                contentWidth = header.Width;

                header.AutoResize(ColumnHeaderAutoResizeStyle.HeaderSize);
                headerWidth = header.Width;

                if (contentWidth > headerWidth)
                    headerResize = ColumnHeaderAutoResizeStyle.ColumnContent;
                else
                    headerResize = ColumnHeaderAutoResizeStyle.HeaderSize;

                header.AutoResize(headerResize);
            }
        }

        //restarts program
        private void ReloadProgram(String newRootAddress)
        {
            systemId = Utility.GetCPUId();
            syncTool = new SyncTool(systemId);
            syncTool.LoadProfile(profile.ProfileName);
            profile = syncTool.GetProfile();
            systemName = profile.SystemName;
            if (systemName == SystemType.Host)
                rootAddress = profile.RootAddressA;
            else
                rootAddress = profile.RootAddressB;
            syncTool.RegisterObserver(this);
            profile.FirstTime = false;
            profile.SecondTime = false;

            difference = null;
            nonConflictList = new List<Difference>();
            conflictList = new List<Difference>();
        }
    }
}
